# -*- coding: utf-8 -*-

"""StaffLog - Terminal
@version: 0.1
@license: GPL 3.0 (see "license" file)
@author: Jose A. Jimenez <jajimc@gmail.com>, Miguel Centeno <miguelcent@gmail.com>"""

import gtk
from opencv.cv import *
from opencv.highgui import *    
    
def detect_face(image):
    """Detects a face on an image and draws a rectangle.
    @param image: Image to process
    @type image: L{opencv.IplImage}
    @return: Face-cropped image, face region (if detected, *None* in other case), left eye region (if detected, *None* in other case), right eye region (if detected, *None* in other case)
    @rtype: (L{opencv.IplImage}, C{list}, C{list}, C{list})"""
    croppedFaceResizedImage = None
    faceRegion = None
    leftEyeRegion = None
    rightEyeRegion = None
    noseRegion = None
    mouthRegion = None
    
    grayscale = cvCreateImage(cvSize(image.width, image.height), 8, 1)  
    cvCvtColor(image, grayscale, CV_BGR2GRAY)
    cvEqualizeHist(grayscale, grayscale)
    
    storage = cvCreateMemStorage(0)
    cvClearMemStorage(storage)
     
    cascade = cvLoadHaarClassifierCascade("haarcascade_frontalface_alt.xml", cvSize(1,1))
    faces = cvHaarDetectObjects(grayscale, cascade, storage, 1.2, 2, CV_HAAR_DO_CANNY_PRUNING, cvSize(50, 50))
    
    if faces and faces[0] != None:
        faceRegion = faces[0]
        
        # Crop image to get the face image
        f = faces[0]
        croppedFaceImage = cvCreateImage(cvSize(int(f.width), int(f.height)), 8, 3)
        region = cvGetSubRect(image, cvRect(int(f.x), int(f.y), int(f.width), int(f.height)))
        cvCopy(region, croppedFaceImage)
        
        # Resize face image
        croppedFaceResizedImage = cvCreateImage(cvSize(200, 200), 8, 3)
        cvResize(croppedFaceImage, croppedFaceResizedImage)
        
        # Detect eyes
        leftEyeRegion = detect_eye(image, croppedFaceImage, f, False)
        rightEyeRegion = detect_eye(image, croppedFaceImage, f, True)
        
        # Detect nose
        noseRegion = detect_nose(image, croppedFaceImage, f)
        
        # Detect mouth
        mouthRegion = detect_mouth(image, croppedFaceImage, f)
        
        # Draw border
        cvRectangle(image, cvPoint(int(f.x), int(f.y)), cvPoint(int(f.x + f.width), int(f.y + f.height)), CV_RGB(255, 0, 0), 3, 8, 0)
        
    return croppedFaceResizedImage, faceRegion, leftEyeRegion, rightEyeRegion, noseRegion, mouthRegion
    
def detect_eye(image, faceImage, faceRegion, rightEye):
    """Detects an eye on an image and draws a rectangle.
    @param image: Image to process
    @type image: L{opencv.IplImage}
    @param faceImage: Image cropped to a face region
    @type faceImage: L{opencv.IplImage}
    @param faceRegion: Region of 'faceImage' inside 'image'
    @type faceRegion: L{opencv.CvRect}
    @param rightEye: *True* if the eye to detect is the right one. *False* in other case.
    @type rightEye: C{bool}
    @return: Eye region if detected. *None* in other case.
    @rtype: C{list}"""
    eyeRegion = None
    
    # Estimate eye's position
    y = int(int(faceImage.height)/5.5)
    width = int(faceImage.width/2)
    height = int(int(faceImage.height)/3.0)
    
    if rightEye: x = 0
    else: x = width

    eyesImage = cvCreateImage(cvSize(width, height), 8, 3)
    region = cvGetSubRect(faceImage, cvRect(x, y, width, height))
    cvCopy(region, eyesImage)
    
    # Detect eye position
    storage = cvCreateMemStorage(0)
    cvClearMemStorage(storage)
    
    cascade = cvLoadHaarClassifierCascade("haarcascade_eye.xml", cvSize(1,1))
    eyes = cvHaarDetectObjects(eyesImage, cascade, storage, 1.5, 3, 0, cvSize(25, 15))
    
    eyeCount = 0
    if eyes:
        e = eyes[0]
        if e != None:
            if rightEye:
                e.x += faceRegion.x
            else:
                e.x += (faceRegion.x + width)
            e.y += (faceRegion.y + y)
            
            eyeRegion = [int(e.x), int(e.y), int(e.x + e.width), int(e.y + e.height)]
            
            # Draw border
            cvRectangle(image, cvPoint(eyeRegion[0], eyeRegion[1]), cvPoint(eyeRegion[2], eyeRegion[3]), CV_RGB(0, 255, 0), 2, 8, 0)
                
    return eyeRegion

def detect_nose(image, faceImage, faceRegion):
    """Detects a nose on an image and draws a rectangle.
    @param image: Image to process
    @type image: L{opencv.IplImage}
    @param faceImage: Image cropped to a face region
    @type faceImage: L{opencv.IplImage}
    @param faceRegion: Region of 'faceImage' inside 'image'
    @type faceRegion: L{opencv.CvRect}
    @return: Nose region if detected. *None* in other case.
    @rtype: C{list}"""
    noseRegion = None
    
    # Estimate nose position
    x = 0
    width = faceImage.width
    height = int(int(faceImage.height)/2.0)
    y = height
    
    noseImage = cvCreateImage(cvSize(width, height), 8, 3)
    region = cvGetSubRect(faceImage, cvRect(x, y, width, height))
    cvCopy(region, noseImage)
    
    # Detect nose position
    storage = cvCreateMemStorage(0)
    cvClearMemStorage(storage)

    cascade = cvLoadHaarClassifierCascade("haarcascade_mcs_nose.xml", cvSize(1,1))
    noses = cvHaarDetectObjects(noseImage, cascade, storage, 1.5, 3, 0, cvSize(25, 15))
    
    if noses:
        n = noses[0]
        if n != None:
            n.x += faceRegion.x
            n.y += (faceRegion.y + y)
            
            noseRegion = [int(n.x), int(n.y), int(n.x + n.width), int(n.y + n.height)]
            
            # Draw border
            cvRectangle(image, cvPoint(noseRegion[0], noseRegion[1]), cvPoint(noseRegion[2], noseRegion[3]), CV_RGB(0, 0, 255), 2, 8, 0)
            
    return noseRegion

def detect_mouth(image, faceImage, faceRegion):
    """Detects a mouth on an image and draws a rectangle.
    @param image: Image to process
    @type image: L{opencv.IplImage}
    @param faceImage: Image cropped to a face region
    @type faceImage: L{opencv.IplImage}
    @param faceRegion: Region of 'faceImage' inside 'image'
    @type faceRegion: L{opencv.CvRect}
    @return: Mouth region if detected. *None* in other case.
    @rtype: C{list}"""
    mouthRegion = None
    
    # Estimate mouth position
    x = 0
    width = faceImage.width
    height = int(int(faceImage.height)/3.0)
    y = height * 2
    
    noseImage = cvCreateImage(cvSize(width, height), 8, 3)
    region = cvGetSubRect(faceImage, cvRect(x, y, width, height))
    cvCopy(region, noseImage)
    
    # Detect mouth position
    storage = cvCreateMemStorage(0)
    cvClearMemStorage(storage)

    cascade = cvLoadHaarClassifierCascade("haarcascade_mcs_mouth.xml", cvSize(1,1))
    mouths = cvHaarDetectObjects(noseImage, cascade, storage, 1.5, 4, 0, cvSize(25, 15))
    
    if mouths:
        m = mouths[0]
        if m != None:
            m.x += (faceRegion.x)
            m.y += (faceRegion.y + y)
            
            mouthRegion = [int(m.x), int(m.y), int(m.x + m.width), int(m.y + m.height)]
            
            # Draw border
            cvRectangle(image, cvPoint(mouthRegion[0], mouthRegion[1]), cvPoint(mouthRegion[2], mouthRegion[3]), CV_RGB(255, 255, 0), 2, 8, 0)
            
    return mouthRegion

def ipl_to_pixbuf(image):
    """Converts an IPL image to a GTK Pixbuf image.
    @param image: Image to convert
    @type image: L{opencv.IplImage}
    @return: GTK Pixbuf copy of input image
    @rtype: L{gtk.gdk.Pixbuf}"""
    color = cvCreateImage(cvSize(image.width, image.height), 8, 3)
    cvConvertImage(image, color, CV_CVTIMG_SWAP_RB)
    
    return gtk.gdk.pixbuf_new_from_data(color.imageData, gtk.gdk.COLORSPACE_RGB, False, color.depth, color.width, color.height, color.widthStep)
    
def get_distances(leftEyeRegion, rightEyeRegion, noseRegion, mouthRegion):
    # Calculate the distance (horizontal) between eyes
    leftEyeX1 = leftEyeRegion[0]
    leftEyeX2 = leftEyeRegion[2]
    leX = leftEyeX1 + (leftEyeX2 - leftEyeX1)
    
    rightEyeX1 = rightEyeRegion[0]
    rightEyeX2 = rightEyeRegion[2]
    reX = rightEyeX1 + (rightEyeX2 - rightEyeX1)
    
    eyesDistance = leX - reX
    
    # Calculate the distances (horizontal and vertical) between left-eye and nose
    noseX1 = noseRegion[0]
    noseX2 = noseRegion[2]
    nX = noseX1 + (noseX2 - noseX1)
    
    leftEyeY1 = leftEyeRegion[1]
    leftEyeY2 = leftEyeRegion[3]
    leY = leftEyeY1 + (leftEyeY2 - leftEyeY1)
    
    noseY1 = noseRegion[1]
    noseY2 = noseRegion[3]
    nY = noseY1 + (noseY2 - noseY1)
     
    horLeftEyeNoseDistance = leX - nX
    verLeftEyeNoseDistance = nY - leY
    
    # Calculate the distance (vertical) between nose and mouth
    mouthY1 = mouthRegion[1]
    mouthY2 = mouthRegion[3]
    mY = mouthY1 + (mouthY2 - mouthY1)
    
    noseMouthDistance = mY - nY
    
    return eyesDistance, horLeftEyeNoseDistance, verLeftEyeNoseDistance, noseMouthDistance

